#include "grid.h"
#include "cell.h"
#include "sudoku.h"
#include "domain.h"

#include <assert.h>

Grid::Grid()
{
    for (int i = 0; i < DOMAIN_SIZE; i++)
    {
        Domain* pd = NULL;

        pd = new Domain();
        m_rows.push_back(pd);

        pd = new Domain();
        m_columns.push_back(pd);

        pd = new Domain();
        m_boxes.push_back(pd);
    }

    for (int row_number = 0; row_number < DOMAIN_SIZE; row_number++)
    {
        for (int column_number = 0; column_number < DOMAIN_SIZE; column_number++)
        {
            Cell* pc = new Cell();
            m_cells.push_back(pc);
            m_rows[row_number]->registerCell(pc);
            m_columns[column_number]->registerCell(pc);

            // Boxes are numbered left to right, top to bottom:
            // 012
            // 345
            // 678
            int box_number = column_number / BOX_WIDTH + (row_number / BOX_WIDTH) * BOX_WIDTH;
            m_boxes[box_number]->registerCell(pc);
        }
    }
}

Grid::~Grid()
{
    for (uint i = 0; i < GRID_SIZE; i++)
        delete m_cells[i];

    for (uint i = 0; i < m_rows.size(); i++)
        delete m_rows[i];

    for (uint i = 0; i < m_columns.size(); i++)
        delete m_columns[i];

    for (uint i = 0; i < m_boxes.size(); i++)
        delete m_boxes[i];
}

int Grid::getSolvedCellCount() const
{
    int value;
    int value_count = 0;

    for (int i = 0; i < GRID_SIZE; i++)
    {
        if (m_cells[i]->getValue(value))
            value_count++;
    }

    return value_count;
}

int Grid::deleteCandidatesByValue()
{
	unsigned int n = m_rows.size();
	assert(m_columns.size() == n);
	assert(m_boxes.size() == n);

	int count = 0;
	for (int v = 1; v <= DOMAIN_SIZE; v++)
	{
		for (unsigned int i = 0; i < n; i++)
		{
			count += m_rows[i]->deleteCandidatesByValue(v);
			count += m_columns[i]->deleteCandidatesByValue(v);
			count += m_boxes[i]->deleteCandidatesByValue(v);
		}
	}
	return count;
}

int Grid::promoteSingleCandidates()
{
	int count = 0;

	for (unsigned int i = 0; i < this->m_cells.size(); i++)
	{
		if (m_cells[i]->promoteSingleCandidate())
			count++;
	}

	return count;
}

bool Grid::runUnitTest()
{
    Grid grid;

    // grid should automatically create correct number
    // of Cells for itself
	int cell_count = grid.m_cells.size();
    if (cell_count != GRID_SIZE)
        return false;

    // Ditto for rows, columns and boxes
    if (grid.m_rows.size() != DOMAIN_SIZE)
        return false;

    if (grid.m_columns.size() != DOMAIN_SIZE)
        return false;

    if (grid.m_boxes.size() != DOMAIN_SIZE)
        return false;

    // No Cell is solved yet
    if (grid.getSolvedCellCount() != 0)
        return false;

	// No Cell has a value, so no candidates can be eliminated
	if (grid.deleteCandidatesByValue() != 0)
		return false;

    // Now we solve top left Cell by fiat
	Cell* cell_top_left = grid.m_cells[0];
    bool retval = cell_top_left->setValue(1);
    if (retval != true)
        return false;

	// One Cell has a value, so that candidate can be eliminated
	// in the corresponding row, column and box.
	// The term 2*... corrects for overlap of boxes with rows & columns
	if (grid.deleteCandidatesByValue() != 3 * (DOMAIN_SIZE - 1) - 2 * (BOX_WIDTH - 1))
		return false;

    // getSolvedCellCount should now return 1
    if (grid.getSolvedCellCount() != 1)
        return false;

	// All nonvalued cells should have more than 1 candidate, so
	// promoteSingleCandidates can't do anything
	if (grid.promoteSingleCandidates() != 0)
		return false;

	// We now delete all candidates but one from bottom right cell
	Cell* cell_bottom_right = grid.m_cells[cell_count - 1];
	for (int v = 1; v < DOMAIN_SIZE; v++)
	{
		if (cell_bottom_right->deleteCandidate(v) != true)
			return false;
	}

	// Now bottom right cell has 1 candidate left, so 
	// promoteSingleCandidates() should be able to promote that
	if (grid.promoteSingleCandidates() != 1)
		return false;

    return true;
}